<!doctype html>
<html lang="zh" class="h-100">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="stylesheet" href="assets/css/bootstrap.min.css">
    <link rel="stylesheet" href="assets/css/style.css">

    <title>UDP协议</title>
  </head>
  <body class="h-100">
  <div class="container-fluid h-100 p-2">
  <h1>UDP协议</h1>
  	<p>UDP协议无连接，不存在onConnect, onClose等事件，数据包发出后，对方是否收到也无法得知。UDP数据包有界，因而也不需要Parser::input检测边界。但是Parser::decode/encode也是有意义的：  </p><p>decode可以把数据包转换成我们便于处理的格式。  <br>encode可以把响应结果转成字节流格式。</p>
  	<h4>实现UDP服务步骤</h4>
  	<ol>
  	
  	<li>从beyod\Handler继承，实现自己的Handler, 并重写onUdpMessage方法，如：
  	
  	<p>app/UdpHandler.php</p><pre><code>&lt;?php
namespace app;

use beyoio\UdpMessageEvent;

class UdpHandler extends \beyoio\Handler
{
    //当收到udp数据包时，此方法被调用。
    public function onUdpMessage(UdpMessageEvent $event)
    {
        $event-&gt;message; //客户发送的数据包内容，具体格式与对应Parser::decode有关
        $event-&gt;local; //服务器地址(ip:port)
        $event-&gt;peer; //对方地址(ip:port)
        $event-&gt;sender; //!!!当前listenner实例
        
        //处理完毕，向客户端发送数据
        //$flag含义参阅stream_socket_sendto函数
        //$raw发送前是否进行封包处理
        $this-&gt;sendto($event, $replyMessage, $flag, $raw);
    }
}
</code></pre>

</li>

<li>从beyod\Parser继承，实现decode/encode方法
<p>Handler收到的消息 $event-&gt;message格式，与Parser::decode返回的类型相同。</p>
</li>

<li>修改配置文件：<p>config/main.php</p>
<pre><code>'components'=&gt;[
    'server'=&gt;[
        'listenners'=&gt;[
            'seny' =&gt;[
                'listen' =&gt; 'udp://0.0.0.0:7512',
                'parser' =&gt; 'app\UdpParser',
                'handler' =&gt; 'app\UdpHandler'
            ]
        ]
    ]
]</code></pre>
</li>
</ol>

<h3>范例：使用udp/unix socket实现一个syslog服务器</h3>
<p>Linux的syslog用于接受本机或其它节点的日志信息，它可以作为客户端以日志中继器的角色存在，也可以作为日志服务器，接收其它客户端的日志信息。</p><p>我们通过Linux系统命令或是syslog函数产生系统日志，本质上是通过unix socket和本机日志服务器通讯，可以用以下命令验证：</p><pre><code>[root@sl ~]# netstat -npl | grep syslog
udp 0  0 0.0.0.0:514 0.0.0.0:*  1099/rsyslogd 

[root@sl ~]# strace  logger &quot;hello syslog&quot;

socket(PF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 1
connect(1, {sa_family=AF_LOCAL, sun_path=&quot;/dev/log&quot;}, 110) = 0
sendto(1, &quot;&lt;13&gt;Sep  8 20:55:22 root: hello &quot;..., 38, MSG_NOSIGNAL, NULL, 0) = 38

[root@sl ~]# file /dev/log 
/dev/log: socket
</code></pre><p>可见，产生系统日志信息，就是向/dev/log套接字发送日志消息。SOCK_DGRAM指明了是用户数据报文协议（无连接，不可靠、固定最大长度）。</p><p>即syslog监听了两个套接字：<br>/dev/log unix domain socket, 基于UDP<br>0.0.0.0:514 UDP端口</p><p>unix:// 提供了在 UNIX 域中对套接字流连接的访问。udg:// 提供了替代的传输器以用户数据报协议（UDP）来访问 UNIX 域套接字。</p><p>也就是unix://是基于连接的(类似TCP), udg://基于无连接的udp传输协议。</p><p>要实现这样的一个服务器，我们首先要弄清楚其数据包格式。</p><p><a href="http://www.ietf.org/rfc/rfc3164.txt">http://www.ietf.org/rfc/rfc3164.txt</a></p><p>beyod自带了syslog服务器，消息格式定义在beyoioprotocolsyslogMessage中，一个syslog日志由以下字段组成：</p><h4>facility</h4><p>日志消息来源设备的标识, 值范围目前为0~23</p><table><thead><tr><th>值</th><th>说明</th></tr></thead><tbody><tr><td>0</td><td>kernel messages</td></tr><tr><td>1</td><td>user-level messages</td></tr><tr><td>2</td><td>mail system</td></tr><tr><td>3</td><td>system daemons</td></tr><tr><td>4</td><td>security/authorization messages</td></tr><tr><td>5</td><td>messages generated internally by syslogd</td></tr><tr><td>6</td><td>line printer subsystem</td></tr><tr><td>7</td><td>network news subsystem</td></tr><tr><td>8</td><td>UUCP subsystem</td></tr><tr><td>9</td><td>clock daemon</td></tr><tr><td>10</td><td>security/authorization messages</td></tr><tr><td>11</td><td>FTP daemon</td></tr><tr><td>12</td><td>NTP subsystem</td></tr><tr><td>13</td><td>log audit</td></tr><tr><td>14</td><td>log alert</td></tr><tr><td>15</td><td>clock daemon (note 2)</td></tr><tr><td>16</td><td>local use 0  (local0)</td></tr><tr><td>17</td><td>local use 1  (local1)</td></tr><tr><td>18</td><td>local use 2  (local2)</td></tr><tr><td>19</td><td>local use 3  (local3)</td></tr><tr><td>20</td><td>local use 4  (local4)</td></tr><tr><td>21</td><td>local use 5  (local5)</td></tr><tr><td>22</td><td>local use 6  (local6)</td></tr><tr><td>23</td><td>local use 7  (local7)</td></tr></tbody></table><h4>severity</h4><p>日志消息的严重程度</p><table><thead><tr><th>值</th><th>说明</th></tr></thead><tbody><tr><td>0</td><td>Emergency: system is unusable</td></tr><tr><td>1</td><td>Alert: action must be taken immediately</td></tr><tr><td>2</td><td>Critical: critical conditions</td></tr><tr><td>3</td><td>Error: error conditions</td></tr><tr><td>4</td><td>Warning: warning conditions</td></tr><tr><td>5</td><td>Notice: normal but significant condition</td></tr><tr><td>6</td><td>Informational: informational messages</td></tr><tr><td>7</td><td>Debug: debug-level messages</td></tr></tbody></table><h4>time</h4><p>日志消息产生的时间，使用GMT时间格式</p><h4>host</h4><p>日志来源主机名。</p><h4>content</h4><p>日志消息内容</p><p>典型的格式如：</p><pre><code>&lt;13&gt;Oct 22 10:52:12 localhost hello, syslog 

&lt;13&gt; 指明了消息来源设备和严重程度,算法为：
serverity =  13 % 8 = 5
facility = 13-5 = 8

Oct 22 10:52:12 是消息产生的时间

localhost是消息来源主机名

hello, syslog 消息内容
</code></pre><p>服务器配置：  <br>config/main.php</p><pre><code>//...
'components'=&gt;[
    'server'=&gt;[
        //...
        'listenners'=&gt;[
            'syslog-udg'=&gt;[
                'listen' =&gt; 'udg://dev/mylog',
                'parser' =&gt; 'beyod\protocol\syslog\Parser'
            ],
            'syslog-udp'=&gt;[
                'listen' =&gt; 'udp://0.0.0.0：514'，
                'parser' =&gt; 'beyod\protocol\syslog\Parser'
            ]
        ]
    ]
]
</code></pre><p>需要自行编写Handler负责消息的最终存储或处理。</p>  </div>
  <script src="assets/js/jquery.min.js"></script>
  <script src="assets/js/popper.min.js"></script>
  <script src="assets/js/bootstrap.min.js"></script>

<script>
$(function(){
  var url = self.location;
  parent.$('a').removeClass('font-weight-bold');
  parent.$('a[href="190.html"]').addClass('font-weight-bold');
});
</script>  
  
  </body>
</html>